package jsu.blogger.service.impl;
import jsu.blogger.config.RedisKeyPrefixConfig;
import jsu.blogger.mapper.UserFollowMapper;
import jsu.blogger.service.inter.UserFollowService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserFollowServiceImpl implements UserFollowService {
    private static final Logger logger = LoggerFactory.getLogger(UserFollowServiceImpl.class);
    @Autowired
    private UserFollowMapper userFollowMapper;

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 添加关注记录并传递自定义时间
    @Transactional
    public boolean insertFollow(Long followerId, Long followedId, LocalDateTime createdAt) {
        if (createdAt == null) {
            createdAt = LocalDateTime.now();  // 如果没有传递时间，使用当前时间
        }
        // 调用Mapper插入数据，返回受影响的行数
        int affectedRows = userFollowMapper.insertFollow(followerId, followedId, createdAt);
        //添加关注数量 和粉丝数量
        // 判断是否成功插入
        return affectedRows > 0 && userFollowMapper.increaseFollowCount(followerId)>0 && userFollowMapper.increaseFollowersCount(followedId)>0;
    }

    // 取消关注
    @Transactional
    public boolean deleteFollow(Long followerId, Long followedId) {
        // 删除关注记录
        int affectedRows = userFollowMapper.deleteFollow(followerId, followedId);
        return affectedRows > 0 && userFollowMapper.decreaseFollowCount(followerId)>0 && userFollowMapper.decreaseFollowersCount(followedId)>0;  // 如果删除成功，返回 true
    }

    // 获取用户关注列表
    public Set<Long> getUserFollowList(Long userId) {
        return userFollowMapper.getUserFollowList(userId);
    }

    // 获取用户粉丝列表
    public Set<Long> getUserFollowersList(Long userId) {
        return userFollowMapper.getUserFollowersList(userId);
    }

    // 创建一个单线程的执行器
    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    // 获取用户的关注列表
    public Set<Long> getUserFollowListByRedis(Long userId) {
        logger.info("进入关注查询>>> ");
        String redisKey = RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_PREFIX + userId;
        // 尝试从 Redis 中获取关注列表
        Set<Object> followListObj = redisTemplate.opsForSet().members(redisKey);  // 返回 Set<Object>
        logger.info("获取关注列表>>> " + followListObj);
        // 如果 Redis 中没有该数据
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
            // 如果 Redis 中没有，查询数据库并缓存数据
            Set<Long> followList = getUserFollowList(userId);
            logger.info("打印数据库数据" + followList);
            // 如果数据库查询结果为空或null，将空集合插入 Redis
            if (followList == null || followList.isEmpty()) {
                followList = Collections.emptySet();
            }
            String lockKey = "lock:user:follow:" + userId;
            RLock lock = redissonClient.getLock(lockKey);
            try {
                if (lock.tryLock() && !Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    logger.info("获取锁>>> ");
                    // 异步插入到 Redis，并设置过期时间
                    Set<Long> finalFollowList = followList;
                    // 异步执行 Redis 插入操作
                    executorService.submit(() -> {
                        try {
                            logger.info("异步实现>>> ");
                            // 异步执行 Redis 插入操作
                            if ( finalFollowList.isEmpty()) {
                                redisTemplate.opsForSet().add(redisKey, "-1");  // 标记集合为空
                            } else {
                                redisTemplate.opsForSet().add(redisKey,
                                        finalFollowList.stream()
                                                .map(String::valueOf)  // 转换 Long 为 String
                                                .toArray(String[]::new));
                            }
                            redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
                            // 你可以在这里做其他后续的异步操作，比如将数据同步到数据库等
                        } catch (Exception e) {
                            // 异常处理，日志记录等
                            logger.error("向 Redis 插入关注列表失败: " + e.getMessage());
                        }
                    });
                }
            } catch (Exception e) {
                logger.error("查询关注表失败"+userId);
                return followList;
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
            return followList;  //获取锁之后的操作是否是异步
        } else {
            // 如果 Redis 中已经存在该数据，则延长过期时间
            redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
        }

        return followListObj.stream()
                .map(obj -> Long.parseLong((String) obj))  // 先转换为 String 再解析为 Long
                .filter(id -> id != -1L)  // 移除 -1
                .collect(Collectors.toSet());
    }

    public Set<Long> getUserFollowerListByRedis(Long userId) {
        logger.info("进入关注查询>>> ");
        String redisKey = RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_PREFIX + userId;
        // 尝试从 Redis 中获取关注列表
        Set<Object> followerListObj = redisTemplate.opsForSet().members(redisKey);  // 返回 Set<Object>
        logger.info("获取粉丝列表>>> " + followerListObj);
        // 如果 Redis 中没有该数据
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
            // 如果 Redis 中没有，查询数据库并缓存数据
            Set<Long> followerList = getUserFollowersList(userId);
            logger.info("打印数据库数据" + followerList);
            // 如果数据库查询结果为空或null，将空集合插入 Redis
            if (followerList == null || followerList.isEmpty()) {
                followerList = Collections.emptySet();
            }
            String lockKey = "lock:user:follower:" + userId;
            RLock lock = redissonClient.getLock(lockKey);
            try {
                if (lock.tryLock() && !Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    // 异步插入到 Redis，并设置过期时间
                    Set<Long> finalFollowerList = followerList;
                    // 异步执行 Redis 插入操作
                    executorService.submit(() -> {
                        try {
                            // 异步执行 Redis 插入操作
                            if (finalFollowerList.isEmpty()) {
                                redisTemplate.opsForSet().add(redisKey, "-1");  // 标记集合为空
                            } else {
                                redisTemplate.opsForSet().add(redisKey,
                                        finalFollowerList.stream()
                                                .map(String::valueOf)  // 转换 Long 为 String
                                                .toArray(String[]::new));
                            }
                            redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_EXPIRE, TimeUnit.MINUTES);  // 设置过期时间
                        } catch (Exception e) {
                            // 异常处理，日志记录等
                            logger.error("向 Redis 插入关注列表失败: " + e.getMessage());
                        }
                    });
                }
            } catch (Exception e) {
                logger.error("查询粉丝表失败"+userId);
                return followerList;
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
            return followerList;  //获取锁之后的操作是否是异步
        } else {
            // 如果 Redis 中已经存在该数据，则延长过期时间
            redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
        }


        return followerListObj.stream()
                .map(obj -> Long.parseLong((String) obj))  // 先转换为 String 再解析为 Long
                .filter(id -> id != -1L)  // 移除 -1
                .collect(Collectors.toSet());
    }

    public boolean setUserFollowKey(Long userId) {
        RLock lock = null;
        try {
            String lockKey = "lock:user:follow:" + userId;
            lock = redissonClient.getLock(lockKey);
            String redisKey = RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_PREFIX + userId;

            // 如果 Redis 中没有该数据
            if (!Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                // 如果 Redis 中没有，查询数据库并缓存数据
                Set<Long> followList = getUserFollowList(userId);
                logger.info(userId + "数据库关注数据" + followList);
                // 如果数据库查询结果为空或null，将空集合插入 Redis
                if (followList == null || followList.isEmpty()) {
                    followList = Collections.emptySet();
                }
                if (lock.tryLock() && !Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    if ( followList.isEmpty()) {
                        redisTemplate.opsForSet().add(redisKey, "-1");  // 标记集合为空
                    } else {
                        redisTemplate.opsForSet().add(redisKey,
                                followList.stream()
                                        .map(String::valueOf)  // 转换 Long 为 String
                                        .toArray(String[]::new));
                    }
                    redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
                }
            } else {
                // 如果 Redis 中已经存在该数据，则延长过期时间
                redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
            }
            return true;
        } catch (Exception e) {
            logger.error("关注表失败"+userId);
            return false;
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public boolean setUserFollowerKey(Long userId) {
        RLock lock = null;
        try {
            String lockKey = "lock:user:follower:" + userId;
            lock = redissonClient.getLock(lockKey);
            String redisKey = RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_PREFIX + userId;
            // 如果 Redis 中没有该数据
            if (!Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                // 如果 Redis 中没有，查询数据库并缓存数据
                Set<Long> followerList = getUserFollowersList(userId);
                logger.info(userId + "打印数据库粉丝数据" + followerList);
                // 如果数据库查询结果为空或null，将空集合插入 Redis
                if (followerList == null || followerList.isEmpty()) {
                    followerList = Collections.emptySet();
                }
                if (lock.tryLock() && !Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    // 如果 followerList 为空，直接缓存空数组
                    if (followerList.isEmpty()) {
                        redisTemplate.opsForSet().add(redisKey, "-1");  // 标记集合为空
                    } else {
                        redisTemplate.opsForSet().add(redisKey,
                                followerList.stream()
                                        .map(String::valueOf)  // 转换 Long 为 String
                                        .toArray(String[]::new));
                    }
                    redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);  // 设置过期时间
                }
            } else {
                // 如果 Redis 中已经存在该数据，则延长过期时间
                redisTemplate.expire(redisKey, RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_EXPIRE, TimeUnit.MINUTES);
            }
            return true;
        } catch (Exception e) {
            logger.error("粉丝表失败"+userId);
            return false;
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    //关注用户
    @Transactional
    public boolean followUser(Long followerId, Long followedId) {
        //关注表
        String userFollowKey = RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_PREFIX + followerId;
        //粉丝表
        String userFollowerKey = RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_PREFIX + followedId;
        try {
            // 获取当前时间
            LocalDateTime now = LocalDateTime.now();
            boolean isSuccess = insertFollow(followerId, followedId, now);
            // 第二步：更新缓存（前提是数据库更新成功）
            if (isSuccess) {
                //都不存在是 不需要执行后续的lua脚本 只需要setUserFollowKey setUserFollowerKey即可
                boolean notNeedExecuteLua = !Boolean.TRUE.equals(redisTemplate.hasKey(userFollowerKey)) && !Boolean.TRUE.equals(redisTemplate.hasKey(userFollowKey));
                //设置redis关注表
                boolean isSetFollowKey = setUserFollowKey(followerId);
                //设置redis粉丝表
                boolean isSetFollowerKey = setUserFollowerKey(followedId);

                //移动 redisSet关注表中添加followedId  同时粉丝表添加followerId  有一个执行成功就成功
                if (isSetFollowKey && isSetFollowerKey) {
                    if (notNeedExecuteLua) {
                        logger.info("用户 {} 成功关注用户 {}", followerId, followedId);
                        return true;
                    }
                    // Lua 脚本，确保关注列表和粉丝列表的操作原子性
                    String luaScript =
                            "local followList = redis.call('SADD', KEYS[1], ARGV[1]); " +
                                    "local followerList = redis.call('SADD', KEYS[2], ARGV[2]); " +
                                    "redis.call('EXPIRE', KEYS[1], ARGV[3]); " +
                                    "redis.call('EXPIRE', KEYS[2], ARGV[3]); " +
                                    "return followList + followerList;";
                    // 执行 Lua 脚本
                    List<String> keys = Arrays.asList(userFollowKey, userFollowerKey);
                    List<String> args = Arrays.asList(followedId.toString(), followerId.toString(), String.valueOf(RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_EXPIRE * 60));  // 1800秒 = 30分钟
                    // 执行脚本
                    Long result = redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class), keys, args.toArray());
                    if (result != null && result > 0) {
                        logger.info("用户 {} 成功关注用户 {}", followerId, followedId);
                        return true;
                    } else {
                        throw new RuntimeException("Redis Lua 脚本执行失败");
                    }
                }
                throw new RuntimeException("设置相应的redis键异常");
            }
            throw new RuntimeException("关注发送异常");
            //return false;
        } catch (Exception e) {
            logger.error(followerId + " 用户关注用户 " + followedId + "失败");
            // 清空 Redis 中的关注和粉丝关系
            try {
                redisTemplate.delete(userFollowKey);
                redisTemplate.delete(userFollowerKey);
            }catch (Exception e1){
                logger.error("redis清除失败请求人工请求数据 {} {}",userFollowerKey,userFollowKey);
            }
            return false;
        }
    }

    @Transactional
    public boolean unfollowUser(Long followerId, Long followedId) {
        // 关注表
        String userFollowKey = RedisKeyPrefixConfig.USER_SET_FOLLOW_KEY_PREFIX + followerId;
        // 粉丝表
        String userFollowerKey = RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_PREFIX + followedId;

        try {
            // 第一步：从数据库中删除关注关系
            boolean isSuccess = deleteFollow(followerId, followedId);
            // 第二步：更新缓存（前提是数据库删除成功）
            if (isSuccess) {
                boolean notNeedExecuteLua = !Boolean.TRUE.equals(redisTemplate.hasKey(userFollowerKey)) && !Boolean.TRUE.equals(redisTemplate.hasKey(userFollowKey));
                boolean isRemoveFollowKey = setUserFollowKey(followerId);
                boolean isRemoveFollowerKey = setUserFollowerKey(followedId);
                // 如果 Redis 中的删除操作都成功，并且不需要执行 Lua 脚本，则直接返回成功
                if (isRemoveFollowKey && isRemoveFollowerKey) {
                    if (notNeedExecuteLua) {
                        logger.info("用户 {} 成功取消关注用户 {}", followerId, followedId);
                        return true;
                    }
                    // Lua 脚本，确保取消关注列表和粉丝列表的操作原子性
                    String luaScript =
                            "local followList = redis.call('SREM', KEYS[1], ARGV[1]); " +
                                    "local followerList = redis.call('SREM', KEYS[2], ARGV[2]); " +
                                    "redis.call('EXPIRE', KEYS[1], ARGV[3]); " +
                                    "redis.call('EXPIRE', KEYS[2], ARGV[3]); " +
                                    "return followList + followerList;";

                    // 执行 Lua 脚本
                    List<String> keys = Arrays.asList(userFollowKey, userFollowerKey);
                    List<String> args = Arrays.asList(followedId.toString(), followerId.toString(), String.valueOf(RedisKeyPrefixConfig.USER_SET_FOLLOWER_KEY_EXPIRE * 60));

                    // 执行脚本
                    Long result = redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class), keys, args.toArray());
                    if (result != null && result > 0) {
                        logger.info("用户 {} 成功取消关注用户 {}", followerId, followedId);
                        return true;
                    } else {
                        throw new RuntimeException("Redis Lua 脚本执行失败");
                    }
                }
                throw new RuntimeException("设置相应的redis键异常");
            }
            throw new RuntimeException("关注删除异常");
            //return false;
        } catch (Exception e) {
            logger.error("用户 {} 取消关注用户 {} 失败", followerId, followedId, e);
            // 清空 Redis 中的关注和粉丝关系
            try {
                redisTemplate.delete(userFollowKey);
                redisTemplate.delete(userFollowerKey);
            }catch (Exception e1){
                logger.error("redis清除失败请求人工请求数据 {} {}",userFollowerKey,userFollowKey);
            }
            return false;
        }
    }

    public boolean isUserFollowing(Long userId, Long targetUserId) {
        return  getUserFollowListByRedis(userId).contains(targetUserId);
    }

    public boolean isUserFollower(Long userId, Long targetUserId) {
        return getUserFollowerListByRedis(userId).contains(targetUserId);
    }

    public boolean isMutualFollow(Long userIdA, Long userIdB) {
        return getUserFollowerListByRedis(userIdA).contains(userIdB) && getUserFollowerListByRedis(userIdB).contains(userIdA);
    }

}
